// window.navigator.userAgent
//判断当前浏览器信息

//封装一个jq的ajax
$.ajax(
    {
        url:"",
        data: {},
        type: "post",
        success: function(data){},
        error: function(data){},
        timeout: 3000
    }
);


// 每次都判断兼容: 利用惰性思想进行浏览器兼容处理
function getXhr() {
    let arr = [
        function () {
            return new ActiveXObject("Microsoft.XMLHTTP");
        },
        function () {
            return new ActiveXObject("Msxml2.XMLHTTP");
        },
        function () {
            return new ActiveXObject("Msxml3.XMLHTTP");
        },
        function () {
            return new XMLHttpRequest()
        }];
    let flag = false;
    let xhr = null;
    for (let i = 0; i < arr.length; i++) {
        let tempXhr = arr[i];
        try {
            //如果不报错那么就说明当前浏览器支持这个方法, 那么我们把getXhr重写
            xhr = tempXhr();
            flag = true;
            //重写getXhr之后,下次再调用getXhr就不会再执行这些循环和判断了
            getXhr = tempXhr;
            break;
        } catch (e) {
            //如果浏览器报错会执行以下代码, 继续循环下一次,把数组中下一个方法执行
            continue;
        }
    }
    //如果循环执行完毕后 flag还是false 那么说明浏览器四个方法都不兼容
    if(!flag){
        throw new Error("浏览器不支持 ajax")
    }
}

/**
 * 获取数据类型
 * @param {*} obj 需要判断类型的数据
 * @param {string} type 数据类型
 * @returns {boolean}
 */
function getType(obj, type) {
    return Object.prototype.toString.call(obj) === "[object " +  type + " ]";
}


/**
 * 把对象格式化为URIString格式
 * @param {*} data 需要格式化的数据
 * @returns {string}
 */
function encodeToURIString(data) {
    if(typeof data === "string"){
        return data;
    }
    if(this.getType(data, "Object")){
        let arr = [];
        for(let key in data){
            //排除非私有属性  data.hasOwnProperty(key)//判断当前对象(data)中是否拥有私有属性(key)
            if(!(data.hasOwnProperty(key))) continue;
            arr.push(encodeURIComponent(n)+ "=" +encodeURIComponent(data[key]))
        }
        return arr.join("&")
    }
    return "";
}

/**
 * 往url后面拼接参数
 * @param {string} url url
 * @param {string} params 参数
 * @returns {string}
 */
function padString(url, params){
    params = this.encodeToURIString(params);
    if(!params){
        return url;
    }
    let hasSearch = /\?/.test(url);
    if(hasSearch){//说明url中已经含有参数了
        return url += "&" + params;
    }else {//说明URL中不含有参数
        return url += "?" + params
    }
}

function myAjax(options) {

    if(!getType(options,"Object")){//判断参数是否合法
        throw new TypeError("参数类型错误");
    }

    // ajax第一步:获取ajax对象
    let xhr = getXhr();


    if(options.data){//如果data有值,直接将data格式化为uriString格式
        options.data = encodeToURIString(options.data)
    }


    //如果是get系,把data拼接到url后面
    if(/^(get|delete|head)$/ig.test(options.type) && options.data){
        options.url = padString(options.url,options.data);
        options.data = "";
    }
    //判断是否走缓存,往url后面加一个随机数
    if(options.cache === false){
        options.url = padString(options.url,"_="+Math.random());//用下划线是为了防止和其他param冲突,使用_当key.因为没人用_当key
    }


    //ajax第二步 :执行open方法
    xhr.open(options.type,
        options.url,
        options.async,
        options.username,
        options.password);

    if(getType(options.headers,"Object")){
        for(var name in options.headers){
            if(!(options.headers.hasOwnProperty(name))){
                continue;
            }
            xhr.setRequestHeader(name,options.headers[name])
        }
    }
    //ajax第三步:注册onreadystatechange事件
    xhr.onreadystatechange = function (){
        if(xhr.readyState === 4){//判断http事务是否完成
            if(/^2\d{2}$/.test(xhr.status)){//判断状态码是否为2xx
                var responseText = xhr.responseText;
                if(options.dataType === "json"){//是否需要把服务器返回的数据格式化为json对象
                    try{//因为不合法json字符串强制格式化为json对象会出现JS异常.所以使用try catch包住
                        JSON.parse(responseText);
                    }catch(ex){
                        options.error && options.error(ex);//如果格式化json对象报错.直接执行error方法.
                        return;//强制终止后续逻辑
                    }
                }
                //执行成功逻辑
                options.success && options.success(responseText);
            }else if(/^(4|5)\d{2}$/.test(xhr.status)){//4xx或5xx 直接抛出错误
                options.error && options.error(xhr.status);
            }
        }
    };

    //处理超时逻辑
    if(getType(options.timeout,"Number")&& options.timeout > 0) {
        if ("ontimeout" in xhr) {//如果xhr对象包含ontimeout属性,超时的话,会强制终止该http事物;
            //timeout表示xhr对象的超时毫秒数
            xhr.timeout = options.timeout;//xhr.timeout 原始ajax自带的超时
            //ontimeout表示过了timeout毫秒.http事务还未完成,则触发timeout事件
            xhr.ontimeout = function () {
                options.error && options.error("timeout");
            }
        } else {
            setTimeout(function () {
                if (xhr.readyState !== 4) {//超时时间已到,但是http事物还未完成
                    //强制终止http事务
                    xhr.abort();
                    options.error && options.error("timeout");
                }
            }, options.timeout)
        }
        //send方法中的参数必须是一个字符串
        xhr.send(options.data);
    }
}

myAjax({
    url:"",
    data: {},
    type: "post",
    success: function(data){},
    error: function(data){},
    timeout: 3000,
    async: true
});